/* Gyroe 1.0.0 */
/* fe_scanner.c : Performs the lexical analysis of
   files. Also handles including of new files, as
   well as storing information of different files. */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "fe_shared.h"
#include "fe_scanner.h"

void init_scanner()
{
    int i;

    /* Initial value for char_class and id_char. */
    for (i = 1;i <= 255;i++) {
        char_class[i] = ILLEGAL_CHAR;
        num_char[i] = N_BAD_NUMBER;
        id_char[i] = 0;
    }

    for (i = '0';i <= '9';i++) {
        char_class[i] = DIGIT;
        num_char[i] = N_NORMAL_NUMBER;
        id_char[i] = 1;
    }

    for (i = 'A';i <= 'Z';i++) {
        char_class[i] = LETTER;
        id_char[i] = 1;
    }

    for (i = 'a';i <= 'z';i++) {
        char_class[i] = LETTER;
        id_char[i] = 1;
    }

    char_class['\t'] = BLANK;
    char_class['\n'] = NEWLINE;
    char_class['\r'] = NEWLINE;
    char_class[' '] = BLANK;
    char_class['!'] = BANG;
    char_class['"'] = DOUBLE_QUOTE;
    char_class['#'] = NUMBER_SIGN;
    char_class['$'] = DOLLAR;
    char_class['&'] = CONCAT;
    char_class['\''] = SINGLE_QUOTE;
    char_class['('] = LEFT_ROUND;
    char_class[')'] = RIGHT_ROUND;
    char_class['*'] = MULTIPLY;
    char_class['+'] = PLUS;
    char_class[','] = COMMA;
    char_class['-'] = MINUS;
    char_class['.'] = DOT;
    char_class['/'] = DIVIDE;
    char_class[':'] = COLON;
    char_class['<'] = LESS;
    char_class['='] = EQUALS;
    char_class['>'] = GREATER;
    char_class['?'] = QUESTION_MARK;
    char_class['['] = LEFT_SQUARE;
    char_class[']'] = RIGHT_SQUARE;
    char_class['{'] = LEFT_BRACE;
    char_class['}'] = RIGHT_BRACE;
    char_class[GY_EOF] = GY_EOF;

    num_char['&'] = N_STOP_CHAR;
    num_char['/'] = N_STOP_CHAR;
    num_char['*'] = N_STOP_CHAR;
    num_char[')'] = N_STOP_CHAR;
    num_char[']'] = N_STOP_CHAR;
    num_char['}'] = N_STOP_CHAR;
    num_char['>'] = N_STOP_CHAR;
    num_char['<'] = N_STOP_CHAR;
    num_char['!'] = N_STOP_CHAR;
    num_char['='] = N_STOP_CHAR;
    num_char[','] = N_STOP_CHAR;
    num_char[' '] = N_STOP_CHAR;
    num_char['\t'] = N_STOP_CHAR;
    num_char['\r'] = N_STOP_CHAR;
    num_char['\n'] = N_STOP_CHAR;
    num_char['.'] = N_DOT;
    num_char['e'] = N_EXPONENT;
    num_char['-'] = N_MINUS;
    num_char['+'] = N_PLUS;

    id_char['_'] = 1;

    bp = 0;

    thisline = malloc(128 * sizeof(int));
    thisline_size = 128;

    string_buffer = malloc(128 * sizeof(int));
    string_buffer_size = 128;

    current_file_no = 0;
    current_source_next = 10000;
    esc_chars = 0;
    gline_number = 0;
    include_space = 0;
    line_number = 0;
}

static int escape_char(int c)
{
    esc_chars++;

    switch (c) {
        case 'n':
            return '\n';
            break;
        case 't':
            return '\t';
            break;
        case '\\':
            return '\\';
            break;
        case 'r':
            return '\r';
            break;
        case '"':
            return '\"';
            break;
        case '\'':
            return '\'';
            break;
        case 'a':
            return '\a';
            break;
        case 'b':
            return '\b';
            break;
        case 'v':
            return '\v';
            break;
        case 'f':
            return '\f';
            break;
        case '/':
            return OS_SLASH;
            break;
        default:
            compile_error("Unknown escape character.");
            break;
    }
}

static char * new_sym()
{
    int i, len;
    char *symbol;

    len = bp_end - bp_begin;

    symbol = malloc(len + 2 * sizeof(char));

    for (i = 0;i <= len;i++)
        symbol[i] = thisline[bp_begin+i];

    symbol[len+1] = '\0';

    return symbol;
}

boolean lex_open(char *file)
{
    if (nest_stack_current == NEST_MAX)
        compile_error("Includes are nested too deep.\n");

    nest_stack_current++;
    nest_stack[nest_stack_current].fptr = fopen(file, "r");

    if (nest_stack[nest_stack_current].fptr != NULL)
        return 1;
    else {
        nest_stack_current--;
        return 0;
    }
}

void lex_close()
{
    fclose(nest_stack[nest_stack_current].fptr);
    nest_stack_current--;
}

/* J.A. -> I'll probably just rewrite this instead of fixing it.
   I really don't want the fe files dealing with s1_ptr's, poke,
   and peek stuff. */
/*
int pack_source(int src)
{
    int start;
    int _1550 = 0;
    int _1, _2;

    //     if equal(src, 0) then
    if (src == 0)
        _1550 = 1;
    else if (IS_ATOM_INT(src) && IS_ATOM_INT(0))
        _1550 = 0;
    else
        _1550 = (compare(src, 0) == 0);
    if (_1550 == 0)
        goto L1;

    // 	return 0
    DeRefi(src);
    return 0;
L1:

    //     if length(src) >= SOURCE_CHUNK then
    DeRef(_1550);
    _1550 = SEQ_PTR(src)->length;
    if (_1550 < 10000)
        goto L2;

    // 	src = src[1..80] -- enough for trace or profile display
    rhs_slice_target = (object_ptr)&src;
    RHS_Slice((s1_ptr)src, 1, 80);
L2:

    //     if current_source_next + length(src) >= SOURCE_CHUNK then
    DeRef(_1550);
    _1550 = SEQ_PTR(src)->length;
    _1550 = _11current_source_next + _1550;
    if (_1550 + HIGH_BITS >= 0)
        _1550 = NewDouble((double)_1550);
    if (binary_op_a(LESS, _1550, 10000))
        goto L3;

    // 	current_source = allocate(SOURCE_CHUNK)
    DeRef(_11current_source);
    _11current_source = _7allocate(10000);

    // 	if current_source = 0 then
    if (binary_op_a(NOTEQ, _11current_source, 0))
        goto L4;

    // 	    CompileErr("out of memory - turn off trace and profile")
    RefDS(_1559);
    _6CompileErr(_1559);
L4:

    // 	all_source = append(all_source, current_source)
    Ref(_11current_source);
    Append(&_11all_source, _11all_source, _11current_source);

    // 	current_source_next = 1
    _11current_source_next = 1;
L3:

    //     start = current_source_next
    start = _11current_source_next;

    //     poke(current_source+current_source_next, src)
    DeRef(_1550);
    if (IS_ATOM_INT(_11current_source)) {
        _1550 = _11current_source + _11current_source_next;
        if (_1550 + HIGH_BITS >= 0)
            _1550 = NewDouble((double)_1550);
    }
    else {
        _1550 = NewDouble(DBL_PTR(_11current_source)->dbl + (double)_11current_source_next);
    }
    if (IS_ATOM_INT(_1550))
        poke_addr = (unsigned char *)_1550;
    else
        poke_addr = (unsigned char *)(unsigned long)(DBL_PTR(_1550)->dbl);
    if (IS_ATOM_INT(src)) {
        *poke_addr = (unsigned char)src;
    }
    else if (IS_ATOM(src)) {
        *poke_addr = (signed char)DBL_PTR(src)->dbl;
    }
    else {
        _1 = (int)SEQ_PTR(src);
        _1 = (int)((s1_ptr)_1)->base;
        while (1) {
            _1 += 4;
            _2 = *((int *)_1);
            if (IS_ATOM_INT(_2))
                *poke_addr++ = (unsigned char)_2;
            else if (_2 == NOVALUE)
                break;
            else {
                *poke_addr++ = (signed char)DBL_PTR(_2)->dbl;
            }
        }
    }

    //     current_source_next += length(src)-1
    DeRef(_1550);
    _1550 = SEQ_PTR(src)->length;
    _1550 = _1550 - 1;
    _11current_source_next = _11current_source_next + _1550;

    //     poke(current_source+current_source_next, 0) -- overwrite \n
    if (IS_ATOM_INT(_11current_source)) {
        _1550 = _11current_source + _11current_source_next;
        if (_1550 + HIGH_BITS >= 0)
            _1550 = NewDouble((double)_1550);
    }
    else {
        _1550 = NewDouble(DBL_PTR(_11current_source)->dbl + (double)_11current_source_next);
    }
    if (IS_ATOM_INT(_1550))
        poke_addr = (unsigned char *)_1550;
    else
        poke_addr = (unsigned char *)(unsigned long)(DBL_PTR(_1550)->dbl);
    *poke_addr = (unsigned char)0;

    //     current_source_next += 1
    _11current_source_next = _11current_source_next + 1;

    //     return start + SOURCE_CHUNK * (length(all_source)-1)
    DeRef(_1550);
    _1550 = SEQ_PTR(_11all_source)->length;
    _1550 = _1550 - 1;
    if (_1550 <= INT15)
        _1550 = 10000 * _1550;
    else
        _1550 = NewDouble(10000 * (double)_1550);
    if (IS_ATOM_INT(_1550)) {
        _1550 = start + _1550;
        if (_1550 + HIGH_BITS >= 0)
            _1550 = NewDouble((double)_1550);
    }
    else {
        _1550 = NewDouble((double)start + DBL_PTR(_1550)->dbl);
    }
    DeRefi(src);
    return _1550;
    ;
}

int AppendSourceLine()
{
    int new = 0, old = 0, options = 0, src = 0;
    int _1581 = 0, _1598 = 0, _1603 = 0;
    int _1, _2;

    //     if TRANSLATE or OpTrace or OpProfileStatement or OpProfileTime then
    if (0 != 0) {
        _1581 = 1;
        goto L1;
    }
    _1581 = (_4OpTrace != 0);
L1:
    if (_1581 != 0) {
        DeRef(_1581);
        _1581 = 1;
        goto L2;
    }
    _1581 = (_4OpProfileStatement != 0);
L2:
    if (_1581 != 0) {
        goto L3;
    }
L4:
    if (_4OpProfileTime == 0)
        goto L5;
L3:

    // 	src = thisline
    Ref(_6thisline);
    DeRefi(src);
    src = _6thisline;

    // 	if ELINUX and TRANSLATE and mybsd then
    if (_4ELINUX == 0) {
        DeRef(_1581);
        _1581 = 0;
        goto L6;
    }
    _1581 = (0 != 0);
L6:
    if (_1581 == 0) {
        goto L7;
    }
L8:
    if (_4mybsd == 0)
        goto L7;

    // 	    src = ""  -- save space, only 8Mb available!
    RefDS(_39);
    DeRefi(src);
    src = _39;
L7:

    // 	if OpTrace then
    if (_4OpTrace == 0)
        goto L9;

    // 	    options = SOP_TRACE
    options = 1;
L9:

    // 	if OpProfileTime then
    if (_4OpProfileTime == 0)
        goto LA;

    // 	    options = or_bits(options, SOP_PROFILE_TIME)
    options = (options | 2);
LA:

    // 	if OpProfileStatement then
    if (_4OpProfileStatement == 0)
        goto LB;

    // 	    options = or_bits(options, SOP_PROFILE_STATEMENT)
    options = (options | 4);
LB:

    // 	if OpProfileStatement or OpProfileTime then
    if (_4OpProfileStatement != 0) {
        goto LC;
    }
LD:
    if (_4OpProfileTime == 0)
        goto LE;
LC:

    // 	    src = {0,0,0,0} & src
    _1 = NewS1(4);
    _2 = (int)((s1_ptr)_1)->base;
    *((int *)(_2+4)) = 0;
    *((int *)(_2+8)) = 0;
    *((int *)(_2+12)) = 0;
    *((int *)(_2+16)) = 0;
    _1581 = MAKE_SEQ(_1);
    if (IS_SEQUENCE(_1581) && IS_ATOM(src)) {
        Ref(src);
        Append(&src, _1581, src);
    }
    else if (IS_ATOM(_1581) && IS_SEQUENCE(src)) {
    }
    else {
        Concat((object_ptr)&src, _1581, (s1_ptr)src);
    }
LE:
L5:
    //     if length(slist) then
    DeRef(_1581);
    _1581 = SEQ_PTR(_4slist)->length;
    if (_1581 == 0)
        goto LF;

    // 	old = slist[$-1]
    _1581 = SEQ_PTR(_4slist)->length;
    _1581 = _1581 - 1;
    DeRef(old);
    _2 = (int)SEQ_PTR(_4slist);
    old = (int)*(((s1_ptr)_2)->base + _1581);
    Ref(old);

    // 	if equal(src, old[SRC]) and
    _2 = (int)SEQ_PTR(old);
    _1581 = (int)*(((s1_ptr)_2)->base + 1);
    Ref(_1581);
    if (src == _1581)
        _1581 = 1;
    else if (IS_ATOM_INT(src) && IS_ATOM_INT(_1581))
        _1581 = 0;
    else
        _1581 = (compare(src, _1581) == 0);
    if (_1581 == 0) {
        _1581 = 0;
        goto L10;
    }
    DeRef(_1598);
    _2 = (int)SEQ_PTR(old);
    _1598 = (int)*(((s1_ptr)_2)->base + 3);
    Ref(_1598);
    if (IS_ATOM_INT(_1598)) {
        _1598 = (_4current_file_no == _1598);
    }
    else {
        _1598 = binary_op(EQUALS, _4current_file_no, _1598);
    }
    if (IS_ATOM_INT(_1598))
        _1581 = (_1598 != 0);
    else
        _1581 = DBL_PTR(_1598)->dbl != 0.0;
L10:
    if (_1581 == 0) {
        DeRef(_1598);
        _1598 = 0;
        goto L11;
    }
    DeRef(_1581);
    _2 = (int)SEQ_PTR(old);
    _1581 = (int)*(((s1_ptr)_2)->base + 2);
    Ref(_1581);
    if (IS_ATOM_INT(_1581)) {
        _1581 = _1581 + 1;
        if (_1581 > MAXINT)
            _1581 = NewDouble((double)_1581);
    }
    else
        _1581 = binary_op(PLUS, 1, _1581);
    DeRef(_1603);
    _1603 = SEQ_PTR(_4slist)->length;
    _2 = (int)SEQ_PTR(_4slist);
    _1603 = (int)*(((s1_ptr)_2)->base + _1603);
    Ref(_1603);
    if (IS_ATOM_INT(_1581) && IS_ATOM_INT(_1603)) {
        _1603 = _1581 + _1603;
        if (_1603 + HIGH_BITS >= 0)
            _1603 = NewDouble((double)_1603);
    }
    else {
        _1603 = binary_op(PLUS, _1581, _1603);
    }
    if (IS_ATOM_INT(_1603)) {
        _1603 = (_4line_number == _1603);
    }
    else {
        _1603 = binary_op(EQUALS, _4line_number, _1603);
    }
    if (IS_ATOM_INT(_1603))
        _1598 = (_1603 != 0);
    else
        _1598 = DBL_PTR(_1603)->dbl != 0.0;
L11:
    if (_1598 == 0) {
        goto L12;
    }
    DeRef(_1581);
    _2 = (int)SEQ_PTR(old);
    _1581 = (int)*(((s1_ptr)_2)->base + 4);
    Ref(_1581);
    if (IS_ATOM_INT(_1581)) {
        _1581 = (options == _1581);
    }
    else {
        _1581 = binary_op(EQUALS, options, _1581);
    }
L13:
    if (_1581 == 0) {
        goto L12;
    }
    else {
        if (!IS_ATOM_INT(_1581) && DBL_PTR(_1581)->dbl == 0.0)
            goto L12;
    }

    // 	    slist[$] += 1
    DeRef(_1581);
    _1581 = SEQ_PTR(_4slist)->length;
    DeRef(_1603);
    _2 = (int)SEQ_PTR(_4slist);
    _1603 = (int)*(((s1_ptr)_2)->base + _1581);
    Ref(_1603);
    if (IS_ATOM_INT(_1603)) {
        _1603 = _1603 + 1;
        if (_1603 > MAXINT)
            _1603 = NewDouble((double)_1603);
    }
    else
        _1603 = binary_op(PLUS, 1, _1603);
    Ref(_1603);
    _2 = (int)SEQ_PTR(_4slist);
    if (!UNIQUE(_2)) {
        _2 = (int)SequenceCopy((s1_ptr)_2);
        _4slist = MAKE_SEQ(_2);
    }
    _2 = (int)(((s1_ptr)_2)->base + _1581);
    _1 = *(int *)_2;
    *(int *)_2 = _1603;
    DeRef(_1);
    goto L14;
L12:

    // 	    src = pack_source(src)
    Ref(src);
    src = _11pack_source(src);

    // 	    new = {src, line_number, current_file_no, options}
    _1 = NewS1(4);
    _2 = (int)((s1_ptr)_1)->base;
    Ref(src);
    *((int *)(_2+4)) = src;
    *((int *)(_2+8)) = _4line_number;
    *((int *)(_2+12)) = _4current_file_no;
    *((int *)(_2+16)) = options;
    new = MAKE_SEQ(_1);

    // 	    if slist[$] = 0 then
    DeRef(_1603);
    _1603 = SEQ_PTR(_4slist)->length;
    _2 = (int)SEQ_PTR(_4slist);
    _1603 = (int)*(((s1_ptr)_2)->base + _1603);
    Ref(_1603);
    if (binary_op_a(NOTEQ, _1603, 0))
        goto L15;

    // 		slist[$] = new
    DeRef(_1603);
    _1603 = SEQ_PTR(_4slist)->length;
    RefDS(new);
    _2 = (int)SEQ_PTR(_4slist);
    if (!UNIQUE(_2)) {
        _2 = (int)SequenceCopy((s1_ptr)_2);
        _4slist = MAKE_SEQ(_2);
    }
    _2 = (int)(((s1_ptr)_2)->base + _1603);
    *(int *)_2 = new;
    goto L16;
L15:

    // 		slist = append(slist, new)
    RefDS(new);
    Append(&_4slist, _4slist, new);
L16:

    // 	    slist = append(slist, 0)
    Append(&_4slist, _4slist, 0);
L17:
    goto L14;
LF:

    // 	src = pack_source(src)
    Ref(src);
    src = _11pack_source(src);

    // 	slist = {{src, line_number, current_file_no, options}, 0}
    _1 = NewS1(4);
    _2 = (int)((s1_ptr)_1)->base;
    Ref(src);
    *((int *)(_2+4)) = src;
    *((int *)(_2+8)) = _4line_number;
    *((int *)(_2+12)) = _4current_file_no;
    *((int *)(_2+16)) = options;
    _1603 = MAKE_SEQ(_1);
    DeRef(_4slist);
    _1 = NewS1(2);
    _2 = (int)((s1_ptr)_1)->base;
    ((int *)_2)[1] = _1603;
    RefDS(_1603);
    ((int *)_2)[2] = 0;
    _4slist = MAKE_SEQ(_1);
L14:

    // end procedure
    DeRef(new);
    DeRef(old);
    DeRefi(src);
    DeRef(_1603);
    DeRef(_1581);
    DeRef(_1598);
    return 0;
    ;
}
*/

void read_line()
{
    gline_number++; line_number++;
    lex_gets();
    bp = 0;
    // AppendSourceLine();
}

char * string_token()
{
    int ch, minus_count;

    SKIP_WHITESPACE

    bp_begin = bp-1;

    while (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r') {
        if (ch == '-') {
            minus_count++;
            if (minus_count == 2) {
                /* Go back one, not two. The final bp--
                   will handle the other '-'. */
                bp--;
                break;
            }
        }
        else
            minus_count = 0;

        GETCH
    }

    bp--;
    bp_end = bp-1;

    return new_sym();
}

char * get_file_path(char *file)
{
    char *fpath;
    int i, j, len;

    len = strlen(file);

    for (i = len;i > 0;i--) {
#ifdef LINUX_OS
        if (file[i] == '/') {
#else
        if (file[i] == '\\' || file[i] == ':') {
#endif
            fpath = malloc((i + 2) * sizeof (char));

            for (j = 0;j <= i;j++)
                fpath[j] = file[j];

            fpath[i+1] = '\0';

            return fpath;
        }
    }

/* Couldn't get a path for some reason. 
   Not a fatal error, so just return a
   filler. */

#ifdef EXTENDING_GYROE
    fprintf(stdio, "Failed to get a path from %s.\n", file);
#endif

    fpath = malloc(3 * sizeof (char));
    fpath[1] = '.';
    fpath[2] = OS_SLASH;
    fpath[3] = '\0';

    return fpath;
}

static void namespace_declaration(int sym)
{
    int h, scope;

    is_defined(sym);
    scope = symtab[sym][S_SCOPE];

    if (scope == SC_GLOBAL || scope == SC_PREDEF) {
        h = symtab[sym][S_HASHVAL];
        sym = new_entry(name_area[symtab[sym][S_NAME]], 0, 0, VARIABLE, h, buckets[h], 0);
        buckets[h] = sym;
    }

    symtab[sym][S_MODE] = M_CONSTANT;
    symtab[sym][S_SCOPE] = SC_LOCAL;
    symtab[sym][S_TOKEN] = NAMESPACE;

    include_space = sym;
}

static boolean same_name(char *a, char *b)
{
#ifdef LINUX_OS
    return !strcmp(a, b);
#elif WINDOWS_OS
    int i;

    if (strlen(a) != strlen(b))
        /* Length isn't equal so strings can't be equal. */
        return 0;

    for (i = 0;i <= strlen(a);i++) {
        if (tolower(a[i]) != tolower(b[i]))
            return 0;
    }

    return 1;
#endif
}

static void scan_new_include()
{
    int ch;
    char *sym;

    SKIP_WHITESPACE
    bp_begin = bp;

    if (ch == '"') {
        /* Quoted file name. Don't use new_str_sym for this. */
        GETCH

        while (ch != '\t' && ch != '\n' && ch != '\r') {
            GETCH
        }

        if (ch != '"')
            compile_error("Syntax error - Missing closing quote on file name.");

        bp_end = bp - 1;

        include_name = new_sym();
    }
    else {
        /* string_token can handle it. Before calling string_token, first
           deincrement bp since string_token calls SKIP_WHITESPACE. */
        bp--;
        include_name = string_token();
    }

    if (!strlen(include_name))
        compile_error("Missing a file name for include.");

    SKIP_WHITESPACE

    if (ch == 'a') {
        GETCH
        if (ch == 's') {
            GETCH
            if (ch == ' ' || ch == '\t') {
                SKIP_WHITESPACE

                bp_begin = bp - 1;

                if (id_char[ch]) {
                    PUSH_VALID_IDENTIFIER

                    bp_end = bp - 2;
                    sym = new_sym();
                    keyfind(sym, -1);
                    if (token_name != VARIABLE && token_name != FUNC &&
                     token_name != PROC && token_name != TYPE)
                        compile_error("A namespace identifier is required here.");

                    namespace_declaration(token_sym);

                }
                else
                    compile_error("Missing namespace qualifier.");
            }
            else
                bp -= 3;
        }
        else
            bp -= 2;
    }
    else
        bp--;
}

static void open_new_include()
{
    /* Interpreter has a new path. Figure out where to open it from.
       At max, four different tests are done :
       1. Absolute
       2. Relative from current path
       3. Relative from GyHome
       4. Relative from GyInclude

       J.A. -> More paths may be added in the future, but this will do
       for now. It allows a program to use copies of the .e files in
       include by itself. Moreover, a user can have a custom set of
       .e files while the ones in GyInclude are shared on a network.
       Keep in mind that GyHome and GyInclude may not be set at all!
       Lastly, enter paths exactly as they are. If the user enters
       an illegal path, a compile_error serves them right. */

    int i, len;

    len = strlen(include_name);

    /* With Linux, assume only '/' is needed first
       to mean absolute path. For Windows, assume
       the second character has to be ':'. */
#ifdef LINUX_OS
    if (include_name[1] == '/') {
#elif WINDOWS_OS
    if (len >= 2 && include_name[2] == ':') {
#endif
        new_file = lex_open(include_name);
        if (new_file)
            return;
        else {
            sprintf(fe_buffer, "Can't open %s", include_name);
            compile_error(fe_buffer);
        }
    }

    /* Second try : Relative from current path.
       (Suggested by c.k. lester) */

    strcpy(fe_buffer, current_path);
    strcat(fe_buffer, include_name);

    new_file = lex_open(fe_buffer);
    if (new_file)
        return;

    if (include_dirs_pos > -1) {
        for (i = 0;i <= include_dirs_pos;i++) {
            strcpy(fe_buffer, include_dirs[i]);
            strcat(fe_buffer, include_name);
            new_file = lex_open(fe_buffer);
            if (new_file)
                return;
        }
    }

    fprintf(stderr, "Can't open %s for including.\n", include_name);

    if (include_dirs_pos > -1) {
        for (i = 0;i <= include_dirs_pos;i++)
            fprintf(stderr, "Tried from %s\n", include_dirs[i]);
    }

    compile_error("File include failed.");
}

static void add_new_include()
{
    int i;

    for (i = 0;i < user_files_pos;i++) {
        if (same_name(fe_buffer, user_files[i])) {
            if (include_space) {
                symtab[include_space][S_OBJ] = i;
                include_space = 0;
            }

            /* Close it and ignore. */
            lex_close();
            return;
        }
    }

    user_files_extra--; user_files_pos++;
    if (user_files_extra == -1) {
        user_files = (char **)realloc(user_files, (user_files_pos+4) * sizeof (char *));
        user_files_extra = 3;
    }

    nest_stack[nest_stack_current].line_no = line_number;
    nest_stack[nest_stack_current].file_start_sym = file_start_sym;
    nest_stack[nest_stack_current].warning = OpWarning;
    nest_stack[nest_stack_current].trace = OpTrace;
    nest_stack[nest_stack_current].type_check = OpTypeCheck;
    nest_stack[nest_stack_current].profile_time = OpProfileTime;
    nest_stack[nest_stack_current].profile_statement = OpProfileStatement;

    file_start_sym = last_sym;

    user_files[user_files_pos] = malloc(strlen(fe_buffer) + 1 * sizeof(char));
    strcpy(user_files[user_files_pos], fe_buffer);
    current_file_no++;

    free(current_path);
    current_path = get_file_path(user_files[user_files_pos]);

    if (include_space) {
        symtab[include_space][S_OBJ] = current_file_no;
        include_space = 0;
    }

    line_number = 0; /* Reset line number for this file. */

    /* Set thisline to the first line in the new file. */
    read_line();
}

void new_include()
{
    scan_new_include();
    open_new_include();
    add_new_include();
}

boolean include_pop()
/* Interpreter is done scanning a file. Hide the locals, close
   it, and set the last file's back into place. */
{
    hide_locals();

    line_number        = nest_stack[nest_stack_current].line_no;
    file_start_sym     = nest_stack[nest_stack_current].file_start_sym;
    OpWarning          = nest_stack[nest_stack_current].warning;
    OpTrace            = nest_stack[nest_stack_current].trace;
    OpTypeCheck        = nest_stack[nest_stack_current].type_check;
    OpProfileTime      = nest_stack[nest_stack_current].profile_time;
    OpProfileStatement = nest_stack[nest_stack_current].profile_statement;

    current_file_no--;

    lex_close();

    if (nest_stack_current == -1)
        /* No more files. */
        return 0;

    /* Keep going. */
    return 1;
}

void lex_gets()
{
    int i, ch;

    ch = getc(nest_stack[nest_stack_current].fptr);

    if (ch == EOF) {
        /* J.A. -> If thisline weren't int *, we couldn't do this. */
        thisline[0] = GY_EOF;
        thisline[1] = GY_EOF;
    }
    else {
	i = 0;
        while (1) {
            if (i == thisline_size) {
                thisline = (int *)realloc(thisline, (thisline_size+8) * sizeof (int));
                thisline_size += 7;
            }
            thisline[i] = ch;
            if (ch == '\n')
                break;
            ch = getc(nest_stack[nest_stack_current].fptr);
            i++;
        }
        i++;
        thisline[i] = '\0';
    }
}

static void preprocess_number()
{
    int ch, class, dec_found, dec_nums, e_found, e_nums, lastch;

    lastch = 0;
    dec_found = 0;
    e_found = 0;

    while (1) {
        GETCH
        class = num_char[ch];
        if (class == N_NORMAL_NUMBER) {
            /* Only thing to do here is mark down if we've found
               numbers after a particular sign. */
            if (dec_found)
                dec_nums = 1;
            if (e_found)
                e_nums = 1;
        }
        else if (class == N_STOP_CHAR) {
            if (dec_found && !dec_nums)
                compile_error("No numbers after the decimal.");
            if (e_found && !e_nums)
                compile_error("No numbers found after the exponent.");
            bp -= 2;
            return;
        }
        else if (class == N_DOT) {
            if (lastch == '.') {
                /* This is a slice, go back so scanner can get it. */
                bp -= 3;

                if (e_found && !e_nums)
                    compile_error("No numbers after the exponent.\n");

                if (!e_found && dec_found != 2)
                    usefp = 0;
                return;
            }
            if (e_found)
                compile_error("Exponents cannot have decimals after them.");
            if (dec_found) {
                /* Could be something like '1.1..2', which is allowed despite precision
                   being ignored. However, make sure it's not '1.2.3..4' since multiple
                   precisions are impossible. */
                GETCH
                if (ch != '.')
                    compile_error("Too many decimals in a number.");
                bp--;
                dec_found++;
            }

            dec_found = 1;

            lastch = N_DOT;
            usefp = 1;
        }
        else if (class == N_EXPONENT) {
            if (dec_found && !dec_nums)
                compile_error("No numbers following the decimal.");
            if (e_found)
                /* Can't have more than one of these... */
                compile_error("Multiple exponent signs in a number.");

            e_found = 1;

            /* The next character could be '+' or '-'. To save trouble,
               pass over it if it's here. */
            GETCH
            if (ch != '-' && ch != '+')
                bp--;
            /* There's a very rare case wherein the minus after the exponent
               could have another minus, thus forming a comment. ('1.3e--comment')
               However, this will be caught by N_MINUS checking for e_nums. */
            usefp = 1;
        }
        else if (class == N_MINUS) {
            if (lastch == '-')
                bp -= 2;
            else
                bp--;

            /* Ensure everything is okay. If it is, exit. */
            if (dec_found && !dec_nums)
                compile_error("No numbers after the decimal.");
            if (e_found && !e_nums)
                compile_error("No numbers found after the exponent.");
            /* Go back before the comment, then leave. */
            return;
        }
        else if (class == N_PLUS) {
            if (lastch == '+')
                compile_error("Sorry, can't (post/pre)increment (yet!)");
            else
                bp -= 2;

            /* Ensure everything is okay. If it is, exit. */
            if (dec_found && !dec_nums)
                compile_error("No numbers after the decimal.");
            if (e_found && !e_nums)
                compile_error("No numbers found after the exponent.");
            /* Go back before the comment, then leave. */
            return;
        }
        else if (class == N_BAD_NUMBER) {
            fprintf(stderr, "Hit a bad number, I guess. ch is `%c`.\n", ch);
            compile_error("Bad character found in number.");
        }

        lastch = ch;
    }
}

static int make_int(char *text)
{
    int i, len, num;

    len = strlen(text);

    if (len > 9 || text[0] == '.') {
        /* len > 9 could overflow integer, and preprocess_number calls this
           if it's something like .19 for some reason. */
        usefp = 1;
        return 0;
    }
        

    num = text[0] - '0';

    if (len > 1) {
        for (i = 1;i < len;i++)
            num = num * 10 + (text[i] - '0');
    }

    return num;
}

long my_sscanf(char *text)
{
    int c, e_mag, e_sign, i, len;
    double num, dec;

    len = strlen(text);

    text[strlen(text)] = 0;
    num = 0.0;
    c = text[0];
    i = 1;

    while (c >= '0' && c <= '9') {
        num = num * 10 + (c - '0');
        c = text[i];
        i++;
    }

    if (c == '.') {
        c = text[i];
        i++;
        dec = 10.0;
        while (c >= '0' && c <= '9') {
            num = num + (c - '0') / dec;
            c = text[i];
            dec = dec * 10.0;
            i++;
        }
    }

    if (c == 'e' || c == 'E') {
        e_mag = 0;
        e_sign = 1;
        c = text[i];
        i++;
        if (c == '-')
            e_sign = -1;
        else if (c != '+')
            i--;
        c = text[i];
        i++;
        if (c >= '0' && c <= '9') {
            e_mag = c - '0';
            c = text[i];
            i++;
            while (c >= '0' && c <= '9') {
                e_mag = e_mag * 10 + c - '0';
                c = text[i];
                i++;
                if (e_mag > 1000)
                    break;
            }
        }
        /// TODO : Find a different way to calculate exponents
        /// that won't overflow or return INF.
        e_mag = e_mag * e_sign;

        if (e_mag > 308) {
            num = num * pow(10.0, 308.0);
            e_mag = e_mag - 308;
            while (e_mag) {
                num = num * 10;
                e_mag--;
            }
        }
        else
            num = num * pow(10.0, e_mag);
    }

    return NewDouble(num);
}

void scanner()
{
    double x;
    int ch, class, i;
    long d;

    while (1) {
        SKIP_WHITESPACE

        class = char_class[ch];

        if (class == LETTER) {
            bp_begin = bp - 1;

            PUSH_VALID_IDENTIFIER

            bp_end = bp - 2;
            bp--;
            yytext = new_sym();
            keyfind(yytext, -1);

            free(yytext);
            /* keyfind will have set token_name and token_sym to new values. */
            if (token_name == NAMESPACE) {
                SKIP_WHITESPACE

                if (ch == ':') {
                    SKIP_WHITESPACE

                    bp_begin = bp-1;

                    /* Don't try doing the push valid identifier thing here.
                       It'll mess this up since it does in-line getch beforehand. */
                    while (id_char[ch]) {
                        GETCH
                    }

                    bp_end = bp - 2;
                    bp--;

                    yytext = new_sym();

                    if (!strlen(yytext))
                        compile_error("A namespace identifier is expected here.");

                    keyfind(yytext, symtab[token_sym][S_OBJ]);
                    free(yytext);
                }
                else {
                    /* J.A. -> Not sure if this should be allowed or not. For now,
                       just put in code to see where it happens. */
                    compile_error("Hit namespace token but no namespace.\n");
                    bp--;
                }
            }
            return;
        }
        else if (class <= ILLEGAL_CHAR || class == EQUALS) {
            token_name = class; token_sym = 0;
            return;
        }
        else if (class == NEWLINE) {
            read_line();
            continue;
        }
        else if (class == DOT || class == DIGIT) {
            if (class == DOT) {
                ch = thisline[bp];

                if (ch == '.') {
                    bp++;
                    token_name = SLICE; token_sym = 0;
                    return;
                }
            }

            usefp = 0;

            bp_begin = bp - 1;
            preprocess_number();
            bp_end = bp;
            bp++;

            yytext = new_sym();

            token_name = ATOM;
            /* No matter what succeeds, token_name will be ATOM.
               Might as well set it here. */

            if (!usefp) {
                i = make_int(yytext);
                /* Note : make_int won't make an int from yytext if
                   yytext has a length > 9. If this happens, it will
                   simply return 0 and set usefp to 1 so my_sscanf
                   is used.
                   Therefore, must check usefp again. */
                if (!usefp) {
                    /* make_int worked. Set values and return. */
                    token_sym = new_int_sym(i);
                    free(yytext);
                    return;
                }
            }

            d = my_sscanf(yytext);
            free(yytext);

            token_sym = new_atom_sym(d);

            return;
        }
        else if (class == MINUS) {
            GETCH

            if (ch == '-') {
                /* Found a comment. Get the next line. */
                read_line();
                continue;
            }
            else if (ch == '=') {
                token_name = MINUS_EQUALS; token_sym = 0;
                return;
            }
            else {
                bp--;
                token_name = MINUS; token_sym = 0;
                return;
            }
        }
        else if (class == DOUBLE_QUOTE) {
            ch = thisline[bp++];
            i = 0;

            while (ch != '"') {
                if (ch == '\\')
                    ch = escape_char(thisline[bp++]);

                if (i == fe_buffer_max) {
                    fe_buffer = (char *)realloc(fe_buffer, (fe_buffer_max * 2) * 
                                                 sizeof (char));
                    fe_buffer_max *= 2;
                }

                fe_buffer[i] = ch;
                ch = thisline[bp++];
                i++;
            }

            if (i == fe_buffer_max) {
                fe_buffer = (char *)realloc(fe_buffer, (fe_buffer_max * 2) * 
                                             sizeof (char));
                fe_buffer_max *= 2;
            }

            fe_buffer[i] = '\0';

            token_name = STRING; token_sym = new_string_sym(fe_buffer);
            return;
        }
        else if (class == PLUS) {
            GETCH

            if (ch == '=')
                token_name = PLUS_EQUALS;
            else {
                token_name = PLUS;
                bp--;
            }

            token_sym = 0;
            return;
        }
        else if (class == CONCAT) {
            GETCH

            if (ch == '=')
                token_name = CONCAT_EQUALS;
            else {
                token_name = CONCAT;
                bp--;
            }

            token_sym = 0;
            return;
        }
        else if (class == MULTIPLY) {
            GETCH

            if (ch == '=')
                token_name = MULTIPLY_EQUALS;
            else {
                token_name = MULTIPLY;
                bp--;
            }

            token_sym = 0;
            return;
        }
        else if (class == DIVIDE) {
            GETCH

            if (ch == '=')
                token_name = DIVIDE_EQUALS;
            else {
                token_name = DIVIDE;
                bp--;
            }

            token_sym = 0;
            return;
        }
        else if (class == LESS) {
            GETCH

            if (ch == '=')
                token_name = LESSEQ;
            else {
                token_name = LESS;
                bp--;
            }

            token_sym = 0;
            return;
        }
        else if (class == GREATER) {
            GETCH

            if (ch == '=')
                token_name = GREATEREQ;
            else {
                token_name = GREATER;
                bp--;
            }

            token_sym = 0;
            return;
        }
        else if (class == BANG) {
            GETCH

            if (ch == '=')
                token_name = NOTEQ;
            else {
                token_name = NOT;
                bp--;
            }

            token_sym = 0;
            return;
        }
        else if (class == SINGLE_QUOTE) {
            ch = thisline[bp++];

            switch (ch) {
                case '\\':
                    ch = escape_char(thisline[bp++]);
                    break;
                case '\'':
                    compile_error("Single-quote character is empty.");
                    break;
            }

            if (thisline[bp++] != '\'')
                compile_error("Character constant is missing a closing ' mark");

            token_name = ATOM; token_sym = new_int_sym(ch);
            return;
        }
        else if (class == NUMBER_SIGN) {
            /* Could be a hex value or a Linux shebang. */

            x = 0;
            usefp = 0;

            while (x < (1073741823 / 32)) {
                GETCH

                if (ch >= '0' && ch <= '9') {
                    x = (x * 16) + (ch - '0');
                    usefp = 1;
                }
                else if (ch >= 'A' && ch <= 'F') {
                    x = (x * 16) + ch - ('A' - 10);
                    usefp = 1;
                }
                else
                    break;
            }

            if (!usefp) {
                if (ch = '!') {
                    if (line_number != 1)
                        compile_error("#! may only be on the first line.");

                    read_line();
                }
                else
                    compile_error("Hex number not formed correctly.");
            }

            if (x >= (1073741823 / 32)) {
                usefp = 0;

                while (1) {
                    GETCH

                    if (ch >= '0' && ch <= '9')
                        x = (x * 16) + ch - '0';
                    else if (ch >= 'A' && ch <= 'F')
                        x = (x * 16) + ch - ('A' - 10);
                    else
                        break;
                }
            }

            bp--;

            if (num_char[thisline[bp]] == N_BAD_NUMBER) {
                /* thisline[bp] is whatever character that the above stuff
                   'broke' on. Make sure the hex didn't break on something
                   it wasn't supposed to. */
                compile_error("Illegal character found in hex.\n");
            }

            token_name = ATOM;

            if (usefp) {
                token_sym = new_int_sym((int)x);
                return;
            }

            if (x <= 1073741823) {
                token_sym = new_int_sym((int)x);
                return;
            }
            else {
                token_sym = new_atom_sym(NewDouble(x));
                return;
            }
        }
        else if (class == GY_EOF) {
            include_pop();
            if (nest_stack_current != -1) {
                read_line();
                continue;
            }
            else {
                token_name = GY_EOF;
                return;
            }
        }
        else {
            fprintf(stderr, "class is %d, ch is %d, bp is %d, thisline[0] is %d\n", class, ch, bp, thisline[0]);
            internal_error("Scanner()");
        }
    }
}

